home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Add-Ons / After Dark / Sort / SortDetails 1.0.p < prev   
Encoding:
Text File  |  1994-11-23  |  17.2 KB  |  585 lines  |  [TEXT/PJMM]

  1. unit SortDetails;
  2. {Written and © by Scott Lindhurst, lindhurs@math.wisc.edu, fall 1993 and Nov. 1994.}
  3. {Mail:    1107 Erin St.        Madison, WI 53715-1845}
  4. {or    123 Millwood Dr.        Tonawanda, NY 14150-5513}
  5. {If you use this source in your program, give me credit in the about box and documentation.}
  6.  
  7.  
  8. {Holds the dirty details of the sorts.}
  9. {Sources: Sedgewick’s Algorithms book and Knuth volume 3.}
  10.  
  11. {Possible additions: make Bubble sort, Shaker sort, Insertion sort into subclasses of one}
  12. {insertion-type-sort class. This may also include Shell sort.}
  13.  
  14. interface
  15.     uses
  16.         PixelUtils, ObjIntf;
  17.  
  18.     type
  19.         cSorter = object(TObject)
  20.                 Done: boolean;        {The procedure that finishes the sort should set this to true}
  21.                 ScreenSize: longint;    {The last position to sort. Saved by Init procedure.}
  22.                 procedure cSorter.Init (N: longint);
  23. {Prepare to sort pixels from 1 to N, inclusive.}
  24.                 procedure cSorter.DoALittle;
  25.             end;
  26.  
  27.         cBatcherSort = object(cSorter)
  28.                 p, q, r, d, savePower, left: longint;
  29.                 InitPLoop: boolean;
  30.                 procedure cBatcherSort.Init (N: longint);
  31.                 override;
  32.                 procedure cBatcherSort.DoALittle;
  33.                 override;
  34.             end;
  35.  
  36.         cBubbleSort = object(cSorter)
  37.                 Bound, left, lastMoved: longint;
  38.                 procedure cBubbleSort.Init (N: longint);
  39.                 override;
  40.                 procedure cBubbleSort.DoALittle;
  41.                 override;
  42.             end;
  43.  
  44.         cShakerSort = object(cSorter)
  45.                 TopBound, BotBound, left, right, lastMoved: longint;
  46.                 goingLeft: boolean;    {true if this pass is going left, false if it’s going right.}
  47.                 procedure cShakerSort.Init (N: longint);
  48.                 override;
  49.                 procedure cShakerSort.DoALittle;
  50.                 override;
  51.             end;
  52.  
  53.         cShellSort = object(cSorter)
  54.                 left, stepSize: longint;
  55.                 stepSizeSorted: boolean;    {true if every subfile {i, i+stepSize, i+2*stepSize,… is sorted}
  56.                 procedure cShellSort.Init (N: longint);
  57.                 override;
  58.                 procedure cShellSort.DoALittle;
  59.                 override;
  60.             end;
  61.  
  62.         cHeapSort = object(cSorter)
  63.                 heapMade: boolean;        {true if the screen has the heap property.}
  64.                 heapSize: longint;        {Size of heap}
  65.                 NextToDo: longint;        {Next position to downHeap or sort}
  66.                 procedure cHeapSort.Init (N: longint);
  67.                 override;
  68.                 procedure cHeapSort.DoALittle;
  69.                 override;
  70.                 procedure cHeapSort.DownHeap (downNode: longint);
  71.             {Move the node down the heap. Depends on heapSize being right.}
  72.             end;
  73.  
  74.         cSmallInsertion = object(cSorter)
  75. {Only gives good multitasking when the screen is almost sorted, i.e. no insertion step}
  76. {takes too long. Designed for use with a QuickSort that ignores small subfiles, like the one below.}
  77.                 left: longint;
  78.                 procedure cSmallInsertion.Init (N: longint);
  79.                 override;
  80.                 procedure cSmallInsertion.DoALittle;
  81.                 override;
  82.             end;
  83.  
  84.         cQuicksort = object(cSorter)
  85. {A quicksort with recursion removed, median-of-three partitioning, and ignoring small}
  86. {subfiles until the end. It should be pretty fast.}
  87.                 doingInsertion: boolean;        {These two are used for the insertion sort of small}
  88.                 insSorter: cSmallInsertion;    {subfiles at the end.}
  89.                 stack: array[0..64] of longint;    {the price we pay for removing recursion}
  90.                 StackTop: longint;        {top of the stack}
  91.                 doingPartition: boolean;    {true if in the middle of a partitioning operation.}
  92.                 ptnLeft, ptnRight, ptnValue: longint;        {left and right pointers if partitioning.}
  93.                 procedure cQuicksort.Init (N: longint);
  94.                 override;
  95.                 function cQuickSort.Partition: longint;
  96. {Using data in SELF, continue the partition or start it.}
  97.                 procedure cQuicksort.DoALittle;
  98.                 override;
  99.             end;
  100.  
  101. implementation
  102.  
  103.     const
  104.         kSliceOps = 1000;    {Aim to do about this many (or fewer) Get or SetPixel per call.}
  105. {SwapPixels counts as 4 ops: 2 Get and 2 Set.}
  106. {SortPair counts as 3 ops: 2 Get, and either 0 or 2 Set.}
  107.         kQSPartitionMin = 10;    {Don’t bother quicksorting anything this short; leave for insertion.}
  108.  
  109.     procedure cSorter.Init (N: longint);
  110.     begin
  111.         Done := false;
  112.         ScreenSize := N;
  113.     end;
  114.  
  115.     procedure cSorter.DoALittle;
  116.     begin
  117.     end;
  118.  
  119.     procedure cBatcherSort.Init (N: longint);
  120.     begin
  121.         p := 1;
  122.         while (p < N) do
  123.             p := 2 * p;
  124.         p := p div 2;        {Now p is the highest power of 2 which is < N}
  125.         savePower := p;
  126.         left := 0;
  127.         InitPLoop := true;    {Yes, we need to start the loop on p.}
  128.         inherited Init(N);
  129.     end;
  130.  
  131.     procedure cBatcherSort.DoALittle;
  132. {Batcher’s merge-exchange method; from Knuth vol. 3 p.112}
  133.         var
  134.             i, right: longint;
  135.     begin
  136.         if InitPLoop then
  137.             begin    {Initialize the loop on p}
  138.                 q := savePower;
  139.                 r := 0;
  140.                 d := p;
  141.                 InitPLoop := false;
  142.             end;
  143.         right := left + kSliceOps div 3;
  144.         if right > ScreenSize - d then
  145.             right := ScreenSize - d;
  146.         for i := left to right do
  147.             if BAnd(i, p) = r then
  148.                 SortPair(i + 1, i + d + 1);
  149.         left := right + 1;
  150.         if right = ScreenSize - d then
  151.             begin    {the loop i:=0 to ScreenSize - d just finished}
  152.                 left := 0;
  153.                 d := q - p;
  154.                 q := q div 2;
  155.                 r := p;
  156.             end;
  157.         if d = 0 then
  158.             begin    {The loop on d just ended; continue the loop on p next time, unless we’re done.}
  159.                 p := p div 2;
  160.                 InitPLoop := true;
  161.                 if p = 0 then
  162.                     Done := true;
  163.             end;
  164.     end;    {Batcher’s sort}
  165.  
  166.  
  167.  
  168.     procedure cShakerSort.Init (N: longint);
  169.     begin
  170.         TopBound := N;    {everything to the right of here is correct}
  171.         BotBound := 1;    {everything to the left of here is correct.}
  172.         left := 1;
  173.         right := N;
  174.         goingLeft := false;
  175.         lastMoved := 0;
  176.         inherited init(N);
  177.     end;    {procedure cShakerSort.Init}
  178.  
  179.     procedure cShakerSort.DoALittle;
  180.         var
  181.             lastMoved, j: longint;
  182.             pix1, pix2: pixelRec;
  183.     begin
  184.         if goingLeft then
  185.             begin
  186.                 left := right - (kSliceOps div 3);
  187.                 if left <= BotBound + 1 then
  188.                     left := BotBound + 1;
  189.                 for j := right downto left do
  190.                     begin
  191.                         pix1 := MyGetPixel(j - 1);
  192.                         pix2 := MyGetPixel(j);
  193.                         if pix1.sortValue > pix2.SortValue then
  194.                             begin
  195.                                 MySetPixel(j - 1, pix2);
  196.                                 MySetPixel(j, pix1);
  197.                                 lastMoved := j;
  198.                             end;
  199.                     end;    {for}
  200.                 right := left - 1;    {prepare for next time}
  201.                 if left = BotBound + 1 then    {Above loop really terminated}
  202.                     if lastMoved = 0 then
  203.                         Done := true
  204.                     else        {Not done; we’ll have to loop again.}
  205.                         begin
  206.                             BotBound := lastMoved;    {stuff to the left of here is sorted.}
  207.                             goingLeft := false;
  208.                             left := BotBound;
  209.                             lastMoved := 0;
  210.                         end;
  211.             end    {going left}
  212.         else    {going right}
  213.             begin
  214.                 right := left + (kSliceOps div 3);
  215.                 if right >= TopBound - 1 then
  216.                     right := TopBound - 1;
  217.                 for j := left to right do
  218.                     begin
  219.                         pix1 := MyGetPixel(j);
  220.                         pix2 := MyGetPixel(j + 1);
  221.                         if pix1.sortValue > pix2.SortValue then
  222.                             begin
  223.                                 MySetPixel(j, pix2);
  224.                                 MySetPixel(j + 1, pix1);
  225.                                 lastMoved := j;
  226.                             end;
  227.                     end;    {for}
  228.                 left := right + 1;    {prepare for next time}
  229.                 if right = TopBound - 1 then    {Above loop really terminated}
  230.                     if lastMoved = 0 then
  231.                         self.Done := true
  232.                     else        {Not done; we’ll have to loop again.}
  233.                         begin
  234.                             TopBound := lastMoved;    {stuff to the right of here is sorted.}
  235.                             goingLeft := true;
  236.                             right := TopBound;
  237.                             lastMoved := 0;
  238.                         end;
  239.             end    {going left}
  240.     end;    {procedure cShakerSort.DoALittle}
  241.  
  242.     procedure cBubbleSort.Init (N: longint);
  243.     begin
  244.         Bound := N;
  245.         left := 1;
  246.         lastMoved := 0;
  247.         inherited init(N);
  248.     end;
  249.  
  250.     procedure cBubbleSort.DoALittle;
  251.         var
  252.             lastMoved, right, j: longint;
  253.             pix1, pix2: pixelRec;
  254.     begin
  255.         right := left + (kSliceOps div 3);
  256.         if right >= Bound then
  257.             right := Bound;
  258.         for j := left to right do
  259.             begin
  260.                 pix1 := MyGetPixel(j);
  261.                 pix2 := MyGetPixel(j + 1);
  262.                 if pix1.sortValue > pix2.SortValue then
  263.                     begin
  264.                         MySetPixel(j, pix2);
  265.                         MySetPixel(j + 1, pix1);
  266.                         lastMoved := j;
  267.                     end;
  268.             end;    {for}
  269.         left := right + 1;    {prepare for next time}
  270.         if right = Bound then    {Above loop really terminated}
  271.             if lastMoved = 0 then
  272.                 self.Done := true
  273.             else        {Not done; we’ll have to loop again.}
  274.                 begin
  275.                     left := 1;
  276.                     Bound := lastMoved - 1;    {everything to the right of here is sorted.}
  277.                     lastMoved := 0;
  278.                 end;
  279.     end;    {procedure cBubbleSort.DoALittle}
  280.  
  281.  
  282.     procedure cShellSort.Init (N: longint);
  283.     begin
  284.         stepSize := 1;
  285.         repeat
  286.             stepSize := 3 * stepSize + 1;
  287.         until stepSize > N;
  288.         stepSizeSorted := true;
  289.         inherited Init(N);
  290.     end;
  291.  
  292.     procedure cShellSort.DoALittle;
  293.         var
  294.             i, j, right: longint;
  295.             savePixel, localPixel: pixelRec;
  296.     begin
  297.         if stepSizeSorted then
  298.             begin
  299.                 stepSize := stepSize div 3;
  300.                 left := stepSize + 1;
  301.                 stepSizeSorted := false;
  302.             end;
  303.         if not stepSizeSorted then
  304.             begin
  305.                 right := left + (kSliceOps div 5);
  306.                 if right >= ScreenSize then
  307.                     begin
  308.                         right := ScreenSize;
  309.                         stepSizeSorted := true;
  310.                     end;
  311.                 for i := left to right do
  312.                     begin
  313.                         savePixel := MyGetPixel(i);
  314.                         j := i;
  315.                         localPixel := MyGetPixel(j - stepSize);
  316.                         while localPixel.sortValue > savePixel.sortValue do
  317.                             begin
  318.                                 MySetPixel(j, localPixel);
  319.                                 j := j - stepSize;
  320.                                 if j <= stepSize then
  321.                                     leave;
  322.                                 localPixel := MyGetPixel(j - stepSize);
  323.                             end;
  324.                         MySetPixel(j, savePixel);
  325.                     end;    {for}
  326.                 left := right + 1;    {update left end to pick up where we left off.}
  327.             end;    {if not stepSizeSorted}
  328.  
  329.         if stepSizeSorted and (stepSize = 1) then
  330.             Done := true;
  331.     end;    {procedure cShellSort.DoALittle}
  332.  
  333.  
  334.  
  335.  
  336.  
  337.     procedure cHeapSort.Init (N: longint);
  338.     begin
  339.         heapMade := false;
  340.         heapSize := N;
  341.         nextToDo := heapSize div 2;
  342.         inherited Init(N);
  343.     end;    {procedure cHeapSort.Init}
  344.  
  345.  
  346.     procedure cHeapSort.DoALittle;
  347.         var
  348.             topToDo, botToDo, k: longint;
  349.     begin
  350.         if not heapMade then
  351.             begin    {downHeap a few things to make it more heaplike.}
  352.                 topToDo := self.nextToDo;
  353.                 botToDo := topToDo - (kSliceOps div 50);
  354.                 self.NextToDo := botToDo - 1;
  355.                 if botToDo <= 1 then
  356.                     begin
  357.                         botToDo := 1;
  358.                         heapMade := true;        {It will be true when the procedure ends.}
  359.                         nextToDo := heapSize;
  360.                     end;
  361.                 for k := topToDo downto botToDo do
  362.                     self.DownHeap(k);
  363.             end    {Making a heap}
  364.         else
  365.     {Pull stuff out of the heap and turn it into sorted material.}
  366.             for k := 1 to kSliceOps div 50 do
  367.                 begin
  368.                     SwapPixels(1, heapSize);
  369.                     heapSize := heapSize - 1;
  370.                     self.DownHeap(1);
  371.                     if heapSize <= 1 then
  372.                         begin
  373.                             self.Done := true;
  374.                             leave;    {the for loop}
  375.                         end;
  376.                 end;    {pulling out and sorting the heap}
  377.     end;        {procedure cHeapSort.DoALittle}
  378.  
  379.  
  380.     procedure cHeapSort.DownHeap (downNode: longint);
  381. {Move the node down the heap.}
  382.         var
  383.             i, j: longint;
  384.             savePixel, pixelJ, pixelJPlus: pixelRec;
  385.     begin
  386.         savePixel := MyGetPixel(downNode);
  387.         while downNode <= (heapSize div 2) do
  388.             begin
  389.                 j := downNode + downNode;
  390.                 pixelJ := MyGetPixel(j);
  391.                 if j < heapSize then
  392.                     begin
  393.                         pixelJPlus := MyGetPixel(j + 1);
  394.                         if pixelJ.sortValue < pixelJPlus.sortValue then
  395.                             begin
  396.                                 pixelJ := pixelJPlus;
  397.                                 j := j + 1;
  398.                             end;
  399.                     end;
  400.                 if savePixel.sortValue >= pixelJ.sortValue then
  401.                     leave;    {done}
  402.                 MySetPixel(downNode, pixelJ);
  403.                 downNode := j;
  404.             end;
  405.         MySetPixel(downNode, savePixel);
  406.     end;    {procedure cHeapSort.DownHeap}
  407.  
  408.  
  409.  
  410.     procedure cSmallInsertion.Init (N: longint);
  411.     begin
  412.         left := 2;
  413.         inherited Init(N);
  414.     end;    {procedure cSmallInsertion.Init}
  415.  
  416.     procedure cSmallInsertion.DoALittle;
  417. {Designed for use on an almost sorted file and called only from QuickSort.}
  418.         var
  419.             i, j, right: longint;
  420.             value, pixelJLeft: PixelRec;
  421.     begin
  422.         right := self.left + (kSliceOps div 5);
  423.         if right >= ScreenSize then
  424.             begin
  425.                 right := ScreenSize;
  426.                 Done := true;
  427.             end;
  428.         for i := left to right do
  429.             begin
  430.                 value := MyGetPixel(i);
  431.                 j := i;
  432.                 pixelJLeft := MyGetPixel(j - 1);
  433.                 while pixelJLeft.SortValue > value.SortValue do
  434.                     begin
  435.                         MySetPixel(j, pixelJLeft);
  436.                         j := j - 1;
  437.                         pixelJLeft := MyGetPixel(j - 1);
  438.                     end;
  439.                 MySetPixel(j, value);
  440.             end;
  441.  
  442.         left := right + 1;    {Update left end to continue next time}
  443.     end;    {procedure cSmallInsertion.DoALittle}
  444.  
  445.  
  446.  
  447.     procedure cQuickSort.Init (N: longint);
  448.         var
  449.             ignore: longint;
  450.     begin
  451.         stack[0] := 1;
  452.         stack[1] := N;
  453.         StackTop := 1;
  454.         doingPartition := false;
  455.         doingInsertion := false;
  456.         New(insSorter);    {for the insertion sort at the end.}
  457.         insSorter.Init(N);
  458.         inherited Init(N);
  459.     end;
  460.  
  461.  
  462.     function cQuickSort.Partition: longint;
  463. {Using data in SELF, continue the partition or start it.}
  464. {The top 2 entries on the stack (Stack array) give the left and right ends of the current partitioning}
  465. {process; the values of ptnLeft and ptnRight are where we are in the partition.}
  466. {If doingPartition = false then initialize (quickly) to do a partition.}
  467. {If doingPartition = true then do some partitioning; if it finishes, then  doingPartition will be}
  468. {    set false and the partition element position will be returned.}
  469. {Note that the return value is defined only if doingPartition is false.}
  470.         var
  471.             left, right, middle: longint;    {for the middle element used in median–of–three partitioning.}
  472.             StopDiff: longint;    {Used to decide when to end a time slice.}
  473.             pixLeft, pixRight: PixelRec;
  474.     begin
  475.         if not doingPartition then
  476.             begin    {Prepare for a new partitioning process. The next call to this will actually do it.}
  477.                 self.doingPartition := true;
  478.                 left := self.Stack[StackTop - 1];
  479.                 right := self.Stack[StackTop];
  480.                 middle := (left + right) div 2;
  481.                 SortThree(left, middle, right);
  482.                 SwapPixels(left + 1, middle);
  483. {Now L <= L+1 <= R  as pixel sort values.}
  484.                 self.ptnLeft := left + 1;
  485.                 self.ptnRight := right;
  486.                 self.ptnValue := MyGetPixel(self.ptnLeft).sortValue;
  487.             end    {preparing to partition}
  488.         else
  489.             begin    {do some partitioning for real!}
  490. {Stop when we have ptnLeft and ptnRight have together moved a distance of kSliceOps}
  491. {i.e. (ptnLeft - origL) + (origR - ptnRight) >= kSliceOps, or}
  492. {origR - origL - kSliceOps >= ptnRight - ptnLeft.}
  493.  
  494.                 StopDiff := ptnRight - ptnLeft - (kSliceOps div 2);
  495.                 repeat
  496.                     repeat
  497.                         ptnLeft := ptnLeft + 1;
  498.                         pixLeft := MyGetPixel(ptnLeft);
  499.                     until pixLeft.sortValue >= ptnValue;
  500.                     repeat
  501.                         ptnRight := ptnRight - 1;
  502.                         pixRight := MyGetPixel(ptnRight);
  503.                     until pixRight.sortValue <= ptnValue;
  504.                     if ptnRight <= ptnLeft then
  505.                         begin    {finished the partitioning!}
  506.                             SwapPixels(Stack[StackTop - 1] + 1, ptnRight);    {Put partition element in place.}
  507.                             leave;
  508.                         end;
  509.                     MySetPixel(ptnLeft, pixRight);    {Swap the pixels.}
  510.                     MySetPixel(ptnRight, pixLeft);
  511.                     if StopDiff >= ptnRight - ptnLeft then
  512.                         leave;
  513.                 until false;    {the only way to leave is with one of the leaves above.}
  514. {Partitioning is done if ptnRight <= ptnLeft, otherwise it’s just suspended.}
  515.                 if ptnRight <= ptnLeft then    {exited above because done}
  516.                     begin    {this is the only case in which a return value is defined.}
  517.                         doingPartition := false;
  518.                         Partition := ptnRight;
  519.                     end;
  520.             end;    {Doing some real partitioning.}
  521.     end;    {function cQuickSort.Partition}
  522.  
  523.  
  524.  
  525.     procedure cQuickSort.DoALittle;
  526. {A quicksort with recursion removed. It assumes that there are <= 2^32 pixels to sort.}
  527. {(Otherwise my stack will overflow}
  528.  
  529.         procedure StackPush (L, R: longint);
  530. {Push L and R onto my stack. Note that other areas depend on this implementation of the}
  531. {stack, so don’t think this is data structure abstraction. It’s only convience.}
  532.         begin
  533.             Stack[StackTop + 1] := L;
  534.             Stack[StackTop + 2] := R;
  535.             StackTop := StackTop + 2;
  536.         end;    {procedure StackPush}
  537.  
  538.         var
  539.             ignore, left, right: longint;    {Left and right endpoints of the current stage.}
  540.             partitionPos: longint;    {Returned by partition, this element is in the right place.}
  541.     begin
  542.         if doingInsertion then
  543.             begin    {Insertion part}
  544.                 insSorter.DoALittle;
  545.                 if insSorter.Done then
  546.                     self.Done := true;
  547.             end    {Insertion part}
  548.         else
  549.             begin    {Quicksort part}
  550.                 if not doingPartition then    {Start partitioning the interval on top of the stack.}
  551.                     ignore := self.Partition
  552.                 else
  553.                     begin
  554.                         partitionPos := self.Partition;    {do some partitioning.}
  555.                         if not doingPartition then    {the partition just finished. Prepare for the next one.}
  556.                             begin    {We’ve finished some partitioning; prepare for the next pieces.}
  557.                                 left := Stack[StackTop - 1];    {pop off old left and right values.}
  558.                                 right := Stack[StackTop];
  559.                                 StackTop := StackTop - 2;
  560.                                 if (partitionPos - left) > (right - partitionPos) then    {If right half smaller, do it first.}
  561.                                     if (right - partitionPos) > kQSPartitionMin then    {As long as it’s not too small.}
  562.                                         begin
  563.                                             StackPush(left, partitionPos - 1);    {Do left later,}
  564.                                             StackPush(partitionPos + 1, right);    {right piece next.}
  565.                                         end    {left half > right half > Min}
  566.                                     else    {right half is small so leave it for later.}
  567.                                         if (partitionPos - left) > kQSPartitionMin then    {if left half big,}
  568.                                             StackPush(left, partitionPos - 1)                    {push it so it gets done.}
  569.                                         else    {left and right both small, so ignore them both.}
  570.                                 else    {left half is smaller, not the right half.}
  571.                                     if (left - partitionPos) > kQSPartitionMin then    {if left half not too small}
  572.                                         begin
  573.                                             StackPush(partitionPos + 1, right);    {push right half to do later}
  574.                                             StackPush(left, partitionPos - 1);    {push left half to do now}
  575.                                         end
  576.                                     else    {left half small}
  577.                                         if (right - partitionPos) > kQSPartitionMin then    {If right half is big…}
  578.                                             StackPush(partitionPos + 1, right);                    {…push it to do next.}
  579.                             end;    {if not doingPartition}
  580.                     end;
  581.                 if StackTop < 0 then    {Done with the quicksort part; start the insertion part now.}
  582.                     doingInsertion := true
  583.             end;    {Quicksort part}
  584.     end;    {Procedure cQuickSort.DoALittle}
  585. end.